package com.own.component.common.util;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

/**
 * PageUtil
 * <p>
 * 数据源分页
 *
 * @author chenxueli
 * @date 2021/6/8 10:04
 */
@Slf4j
public class PageSimulateUtil {

    /**
     * 循环遍历分页
     *
     * @param page 页数
     * @param rows 每页数量
     * @param list 数据源列表
     * @param <T>  列表的对象类型
     * @return 分页后的列表
     */
    public static <T> List<T> cyclePage(int page, int rows, List<T> list) {
        // 数量填充
        var end = page * rows;
        var resultList = new ArrayList<T>();
        do {
            resultList.addAll(list);
        } while (resultList.size() < end);
        var start = (page - 1) * rows;
        return resultList.subList(start, end);
    }

    /**
     * 分页
     *
     * @param page 页数
     * @param rows 每页数量
     * @param list 数据源列表
     * @param <T>  列表的对象类型
     * @return 分页后的列表
     */
    public static <T> List<T> page(int page, int rows, List<T> list) {
        return page(page, rows, false, list);
    }

    /**
     * 分页
     *
     * @param page         页数
     * @param rows         每页数量
     * @param needLastPage 如果分页数据超过最大的页数是否返回最后一页
     * @param list         数据源列表
     * @param <T>          列表的对象类型
     * @return 分页后的列表
     */
    public static <T> List<T> page(int page, int rows, boolean needLastPage, List<T> list) {
        var length = list.size();
        var start = (page - 1) * rows;
        if (length < start) {
            return needLastPage ? list.subList(length - rows, length) : new ArrayList<>();
        }
        var total = Math.min(page * rows, length);
        return list.subList(start, total);
    }

    /**
     * 合并并循环遍历分页
     *
     * @param page        页数
     * @param rows        每页数量
     * @param keyFunction key键的生成方法
     * @param list1       数据源列表1
     * @param list2       数据源列表2
     * @param <T1>        列表1的对象类型
     * @param <T2>        列表2的对象类型
     * @param <KEY>       key的类型
     * @return 分页后的列表
     */
    public static <T1, T2, KEY> LinkedHashMap<KEY, ListObject<T1, T2>> cyclePage(
            int page,
            int rows,
            @NonNull KeyFunction<KEY, T1, T2> keyFunction,
            @NonNull List<T1> list1,
            @NonNull List<T2> list2
    ) {
        var end = page * rows;
        // 如果total=0直接返回空对象
        if (end == 0) {
            return new LinkedHashMap<>();
        }
        // 如果分页数据
        var resultList = new ArrayList<ListObject<T1, T2>>();
        do {
            list1.forEach(item1 -> list2.forEach(item2 -> resultList.add(new ListObject<>(item1, item2))));
        } while (resultList.size() < end);
        var start = (page - 1) * rows;
        var resultMap = new LinkedHashMap<KEY, ListObject<T1, T2>>();
        resultList.subList(start, end).forEach(item -> resultMap.put(keyFunction.get(item.o1, item.o2), item));
        return resultMap;
    }


    /**
     * 合并并循环遍历分页
     *
     * @param page        页数
     * @param rows        每页数量
     * @param keyFunction key键的生成方法
     * @param list1       数据源列表1
     * @param list2       数据源列表2
     * @param <T1>        列表1的对象类型
     * @param <T2>        列表2的对象类型
     * @param <KEY>       key的类型
     * @return 分页后的列表
     */
    public static <T1, T2, KEY> LinkedHashMap<KEY, ListObject<T1, T2>> page(
            int page,
            int rows,
            @NonNull KeyFunction<KEY, T1, T2> keyFunction,
            @NonNull List<T1> list1,
            @NonNull List<T2> list2
    ) {
        return page(page, rows, false, keyFunction, list1, list2);
    }

    /**
     * 合并并循环遍历分页
     *
     * @param page         页数
     * @param rows         每页数量
     * @param needLastPage 如果分页数据超过最大的页数是否返回最后一页
     * @param keyFunction  key键的生成方法
     * @param list1        数据源列表1
     * @param list2        数据源列表2
     * @param <T1>         列表1的对象类型
     * @param <T2>         列表2的对象类型
     * @param <KEY>        key的类型
     * @return 分页后的列表
     */
    public static <T1, T2, KEY> LinkedHashMap<KEY, ListObject<T1, T2>> page(
            int page,
            int rows,
            boolean needLastPage,
            @NonNull KeyFunction<KEY, T1, T2> keyFunction,
            @NonNull List<T1> list1,
            @NonNull List<T2> list2
    ) {
        var length = list1.size() * list2.size();
        int start = (page - 1) * rows;
        int end = page * rows;
        if (length < start) {
            if (needLastPage) {
                end = length;
                start = length - rows;
            } else {
                return new LinkedHashMap<>();
            }
        }
        var resultList = new ArrayList<ListObject<T1, T2>>();
        list1.forEach(item1 -> list2.forEach(item2 -> resultList.add(new ListObject<>(item1, item2))));
        var resultMap = new LinkedHashMap<KEY, ListObject<T1, T2>>();
        resultList.subList(start, end).forEach(item -> resultMap.put(keyFunction.get(item.o1, item.o2), item));
        return resultMap;
    }

    public interface KeyFunction<KEY, OBJECT1, OBJECT2> {
        /**
         * 获取key
         *
         * @param o1 对象1
         * @param o2 对象2
         * @return key值
         */
        KEY get(OBJECT1 o1, OBJECT2 o2);
    }

    @AllArgsConstructor
    @Getter
    @Setter
    public static class ListObject<OBJECT1, OBJECT2> {
        OBJECT1 o1;
        OBJECT2 o2;
    }

}
